﻿///
/// Author: Lee Gary 
///
/// Date: 24/09/2010
///
/// Purpose: 
///
/// The actual form that the users see/use, might not be the best way to do many things but with
/// only 5 days to develop on, it's satisfactory for me.
/// 
/// 2 tree view side-by-side with some labels/textboxes/dropdown list for users to enter necessary input
/// Drag-and-drop functionalities which utilizes GSPMigrationCore to do the actual migration of files
/// unlike Form, which only do the copy visually/superficially
/// 
///  
/// Future:
/// 1.) Implement proper validation
/// 2.) Background worker to improve user experience (when loading big File Share or 
///     SharePoint Document libraries)
/// 3.) Probably some eye candy (.gif) during slow SharePoint web service calls and 
///     File Share eager loading 
///     
/// 
                    /// 4.) Implement lazy-loading to folder level for SharePoint tree view (currently it is lazy
                    ///
///Completed        ///     only to the document library level, so if there are 50 document libraries it will still
                    ///     be very fast, but if a document library have 100 folders with 100 files each, upon
                    ///     expanding it will retrieve all 1000 records... via soap.. = v slow)
/// 5.) Support drag and drop within FileShare (copying/moving files) maybe a context menu pop 
///     up for user to choose
/// 6.) Support drag and drop from SharePoint to FileShare (2 way migration)
/// 7.) Support drag and drop within SharePoint (copying files)
/// 8.) Ultimate challenge is to find ways to migrate all the permissions into SharePoint,
///     extraction is achieved, however no out-of-the-box ways to do it via web service
///     only via Object Model... creating customised web services defeats the purpose..
/// 9.) Ability to do scheduled migration and do migration plans
/// 10.) Ability to run pre-migration tests and simulated migration to forsee problems
/// 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using GSPMigratorCore.GSPObjects;
using GSPMigratorCore.Helper;
using GSPMigratorCore.Misc;
using GSPMigratorCore.NamingConventions;
using GSPMigratorCore.UNC;
using WinApp.Common;

namespace WinApp
{
    public partial class Form2 : BaseForm
    {
        #region global variables
        ImageList il = new ImageList();
        WebserviceHelper WSHelper = new WebserviceHelper();
        public delegate void Add(GSPTreeNode parentNode, GSPTreeNode childNode);
        #endregion

        public Form2()
        {
            InitializeComponent();
        }

        private void Form2_Load(object sender, EventArgs e)
        {
            il.Images.Add(Image.FromFile(GSPConstants.ImagePath.GenericFile));
            il.Images.Add(Image.FromFile(GSPConstants.ImagePath.Folder));
            il.Images.Add(Image.FromFile(GSPConstants.ImagePath.DocumentLibrary));
            il.Images.Add(Image.FromFile(GSPConstants.ImagePath.SPSite));
            il.Images.Add(Image.FromFile(GSPConstants.ImagePath.FileServer));            
        }       

        #region tree view events

        //on user click, clone the nodes to be dropped onto sharepoint treeview
        private void tvFS_MouseDown(object sender, MouseEventArgs e)
        {
            TreeView tree = (TreeView)sender;
            GSPTreeNode node = new GSPTreeNode();
            try
            {
                if (node != null)
                {
                    tree = (TreeView)sender;
                    node = (tree.GetNodeAt(e.X, e.Y) as GSPTreeNode);
                    tree.SelectedNode = node;
                    tree.DoDragDrop(node.Clone(), DragDropEffects.Copy);
                }
            }
            catch (Exception ex)
            {
                WriteLog(LogLevel.Error, "UException: " + ex.ToString() + "Stack trace: " + ex.StackTrace);
            }
        }

        //when nodes are expanded, do a web service call to retrieve real time data
        private void tvSP_BeforeExpand(object sender, TreeViewCancelEventArgs e)
        {
            expandSP(e.Node as GSPTreeNode);
        }       

        //on user drop, call SPBackgroundWorker to do asynchronous migration
        private void tvSP_DragDrop(object sender, DragEventArgs e)
        {
            TreeView tree = (TreeView)sender;
            Point pt = new Point(e.X, e.Y);
            pt = tree.PointToClient(pt);

            GSPTreeNode node = (tree.GetNodeAt(pt) as GSPTreeNode);
            GSPTreeNode newNode = (e.Data.GetData(typeof(GSPTreeNode)) as GSPTreeNode);
         
            node.Nodes.Clear();
            node.Nodes.Add(newNode);
            GSPTreeNode[] nodes = { node, newNode };
            SPBackgroundWorker.RunWorkerAsync(nodes);
        }

        private void tvSP_DragOver(object sender, DragEventArgs e)
        {
            TreeView tree = (TreeView)sender;
            e.Effect = DragDropEffects.Copy;

            if (e.Data.GetData(typeof(GSPTreeNode)) != null)
            {
                Point pt = new Point(e.X, e.Y);
                pt = tree.PointToClient(pt);
                GSPTreeNode node = (tree.GetNodeAt(pt) as GSPTreeNode);
                if (node != null)
                {
                    e.Effect = DragDropEffects.Copy;
                    tree.SelectedNode = node;
                }
            }
        }
              
        #endregion

        #region button event handlers

        private void btnLoadFS_Click(object sender, EventArgs e)
        {
            this.btnLoadFS.Enabled = false;

            if (this.tbRootDir.Text.IsValidGSPSourceDir())
            {
                FillFSTree(); 
            }
            else
            {
                MessageBox.Show(GSPValidationHelper._INVALID_FS_PATH);
                this.btnLoadFS.Enabled = true;
            }
         
        }

        private void btnLoadSP_Click(object sender, EventArgs e)
        {
            tvSP.Nodes.Clear();
            WebserviceHelper WSHelper = new WebserviceHelper(this.tbSPUrl.Text, this.tbSPAccount.Text,
                this.tbSPPassword.Text, this.tbSPDomain.Text, this.rbFBA.Checked);

            List<GSPDocLib> DocLib = WSHelper.GetAllDocLib(this.cBoxHideDocLib.Checked);          
            LoadAllDocLib(DocLib);
        }

        #endregion

        #region load sharepoint lib nodes

        internal void LoadAllDocLib(List<GSPDocLib> docLibs)
        {
            tvSP.Nodes.Clear();
            tvSP.ImageList = il;
            GSPTreeNode rootnode = new GSPTreeNode(this.tbSPUrl.Text);
            rootnode.ImageIndex = GSPConstants.ImagePathIndex.SPSiteIndex;
            rootnode.SelectedImageIndex = GSPConstants.ImagePathIndex.SPSiteIndex;
            tvSP.Nodes.Add(rootnode);
            if (docLibs != null)
            {
                foreach (GSPDocLib docLib in docLibs)
                {
                    GSPTreeNode tempNode = new GSPTreeNode(docLib.Title);
                    tempNode.ImageIndex = GSPConstants.ImagePathIndex.DocumentLibraryIndex;
                    tempNode.SelectedImageIndex = GSPConstants.ImagePathIndex.DocumentLibraryIndex;
                    tempNode.Tag = docLib.WebFullUrl;
                    tempNode.Nodes.Add(GSPConstants.TreeView.LazyLoadedChild);
                    rootnode.Nodes.Add(tempNode);
                }
            }
            tvSP.Nodes[0].Expand();
        }

        #endregion

        #region multithreaded File share loading methods

        private void FSBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            RecurseFS(e.Argument as GSPTreeNode);
        }

        private void FillFSTree()
        {
            tvFS.ImageList = il;
            tvFS.Nodes.Clear();
            IntPtr token;
            try
            {
                if (GSPUNCHelper.ConnectUser(this.tbFSAccount.Text, this.tbFSDomain.Text, this.tbFSPassword.Text, out token))
                {                    
                    GSPTreeNode rootnode = new GSPTreeNode(this.tbRootDir.Text);
                    rootnode.ImageIndex = GSPConstants.ImagePathIndex.FileServerIndex;
                    rootnode.SelectedImageIndex = GSPConstants.ImagePathIndex.FileServerIndex;
                    tvFS.Nodes.Add(rootnode);
                    FSBackgroundWorker.RunWorkerAsync(rootnode);    
                }
            }
            catch (Win32Exception w32ex)
            {
                MessageBox.Show(w32ex.Message);
                this.btnLoadFS.Enabled = true;
            }
        }

        //delegate
        public void Add1(GSPTreeNode parentNode, GSPTreeNode childNode)
        {
            parentNode.Nodes.Add(childNode);
        }

        private void RecurseFS(GSPTreeNode node)
        {
            try
            {
                FileInfo[] files;
                DirectoryInfo rootDir = new DirectoryInfo(node.FullPath);
                files = rootDir.GetFiles();
            
                foreach (FileInfo file in files)
                {
                    try
                    {
                        GSPTreeNode tempNode = new GSPTreeNode(file.Name);
                        tempNode.ImageIndex = GSPConstants.ImagePathIndex.GenericFileIndex;
                        tempNode.SelectedImageIndex = GSPConstants.ImagePathIndex.GenericFileIndex;
                        tempNode.Tag = GSPConstants.TreeView.ReservedFileNameSuffix +
                            GSPConstants.TreeView.TreeViewDelimiter[0] + file.FullName;
                        tempNode.ToolTipText = tempNode.Tag.ToString();
                        tvFS.Invoke(new Add(Add1), new object[] { node, tempNode });
                    }
                    catch (PathTooLongException PTLex)
                    {                        
                        WriteLog(LogLevel.Error, "PException11: " + rootDir.FullName + @"\" + file.Name);
                    }
                    catch (Exception ex)
                    {
                        WriteLog(LogLevel.Error, "UException: " + file.Name);
                    }
                }
                DirectoryInfo[] dirs = rootDir.GetDirectories();
                foreach (DirectoryInfo dir in dirs)
                {
                    GSPTreeNode tempNode = new GSPTreeNode(dir.Name);
                    try
                    {
                        tempNode.ImageIndex = GSPConstants.ImagePathIndex.FolderIndex;
                        tempNode.SelectedImageIndex = GSPConstants.ImagePathIndex.FolderIndex;
                        tempNode.Tag = dir.FullName;
                        tempNode.ToolTipText = tempNode.Tag.ToString();
                        tvFS.Invoke(new Add(Add1), new object[] { node, tempNode });
                    }
                    catch (PathTooLongException PTLEx)
                    {
                        WriteLog(LogLevel.Error, "PException11: " + dir.Name);
                    }
                    catch (Exception ex)
                    {
                        WriteLog(LogLevel.Error, "UException: " + dir.Name);
                    }
                    RecurseFS(tempNode);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void FSBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MessageBox.Show("Completed loading file share");
            this.btnLoadFS.Enabled = true;
        }
        
        #endregion

        #region multithreaded SharePoint webservice 

        private void RecursiveNodeCall(GSPTreeNode parentNode, GSPTreeNode newNode)
        {
            WebserviceHelper WSHelper = new WebserviceHelper(this.tbSPUrl.Text,
                this.tbSPAccount.Text, this.tbSPPassword.Text, this.tbSPDomain.Text, this.rbFBA.Checked);
            if (!newNode.Tag.ToString().StartsWith(GSPConstants.TreeView.ReservedFileNameSuffix))
            {
                #region folder creation logic
                //top node
                string folderTaxonomy = GSPReplacer.ExtractFolderTaxonomy(parentNode.FullPath, this.tbSPUrl.Text);
                folderTaxonomy = folderTaxonomy + @"/" + newNode.Text;
                try
                {
                    folderTaxonomy = GSPReplacer.GetSharePointFriendlyName(folderTaxonomy, this.tbSPUrl.Text.Length,
                        GSPReplacer.MAXFOLDERLENGTH, this.cBoxReplace.Text);

                    #region upload to sharepoint
                    string errorMsg = WSHelper.CreateFolder(folderTaxonomy);

                    if (string.Equals(@"<Result/>", errorMsg)) //creates folder using OOTB ws
                    {
                        string logMsg = newNode.FullPath;
                        WriteLog(LogLevel.Info, string.Format("Info: Create folder success: {0}", logMsg));
                    }
                    else if (string.Equals("<Error ID=\"13\">AlreadyExists</Error>", errorMsg))
                    {
                        WriteLog(LogLevel.Info, string.Format("SPException3: {0}", newNode.FullPath));
                    }
                    else
                    {
                        WriteLog(LogLevel.Error, "SPException1: " +
                            newNode.Tag.ToString() + "Details: " + errorMsg);
                    }
                    #endregion
                }
                catch (ApplicationException AE)
                { }               
               foreach (GSPTreeNode tn in newNode.Nodes)
               {
                   RecursiveNodeCall(newNode, tn);
               }
                #endregion
            }
            else
            {
                #region file upload

                string fileName = newNode.Text;
                fileName = GSPReplacer.GetSharePointFriendlyName(fileName, this.tbSPUrl.Text.Length,
                    GSPReplacer.MAXFOLDERLENGTH, this.cBoxReplace.Text);

                string folderTaxonomy = GSPReplacer.ExtractFolderTaxonomyWOFileName(newNode.FullPath, this.tbSPUrl.Text);
                folderTaxonomy = folderTaxonomy.Replace(@"/" + fileName, "");
                folderTaxonomy = GSPReplacer.GetSharePointFriendlyName(folderTaxonomy, this.tbSPUrl.Text.Length,
                    GSPReplacer.MAXFOLDERLENGTH, this.cBoxReplace.Text);

                
                string fileDestinationUrl = GSPReplacer.ReplaceDoubleSlashes(newNode.FullPath);
                fileDestinationUrl = this.tbSPUrl.Text + folderTaxonomy + @"/" + fileName;

                try
                {
                    try
                    {
                        string errorMsg = WSHelper.UploadFile(newNode.Tag.ToString().Split(GSPConstants.TreeView.TreeViewDelimiter,
                                                StringSplitOptions.None)[1], fileDestinationUrl);
                        if (errorMsg.StartsWith("Success"))
                        {
                            WriteLog(LogLevel.Info, "Upload file success: " +
                                newNode.Tag.ToString().Split(GSPConstants.TreeView.TreeViewDelimiter, StringSplitOptions.None)[1]);
                        }
                        else
                        {
                            WriteLog(LogLevel.Error, "SPException2: " +
                                newNode.Tag.ToString().Split(GSPConstants.TreeView.TreeViewDelimiter,
                                StringSplitOptions.None)[1] + " Details: " + errorMsg);
                        }
                    }
                    catch (GSPException GSPEx)
                    {
                        //TODO: base manager handle ex
                        if (GSPEx.Code == "FS001")
                        {
                            //TODO: log empty file cannot be uploaded to sharepoint
                        }
                    }
                }
                catch (ApplicationException AEX)
                { }
                #endregion
            }
        }

        private void SPBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            GSPTreeNode[] nodes = e.Argument as GSPTreeNode[];
            //old node and new node
            RecursiveNodeCall(nodes[0], nodes[1]); 
        }

        private void SPBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MessageBox.Show("Migration complete!");
        }
        
        private void expandSP(GSPTreeNode node)
        {
            WebserviceHelper WSHelper = new WebserviceHelper(this.tbSPUrl.Text, this.tbSPAccount.Text,
                       this.tbSPPassword.Text, this.tbSPDomain.Text, false);

            if (node.ImageIndex == GSPConstants.ImagePathIndex.FolderIndex)
            {
                node.Nodes.Clear();
                node.Nodes.Add(GSPConstants.TreeView.LazyLoadedChild);
            }

            if (node.ImageIndex == GSPConstants.ImagePathIndex.DocumentLibraryIndex)
            {
                node.Nodes.Clear();
                node.Nodes.Add(GSPConstants.TreeView.LazyLoadedChild);
            } 

            foreach (TreeNode tempNode in node.Nodes)
            {
                if (tempNode.Text == GSPConstants.TreeView.LazyLoadedChild)
                {
                    tvSP.Nodes.Remove(tempNode);
                }
            }

            if (node.ImageIndex != GSPConstants.ImagePathIndex.SPSiteIndex) 
            {
                List<GSPFolder> GSPFolders = new List<GSPFolder>();
                List<GSPListItem> GSPListItems = new List<GSPListItem>();
                string folderTaxonomy = GSPReplacer.ExtractFolderTaxonomy(node.FullPath, this.tbSPUrl.Text);
                string docLib = GSPReplacer.ExtractDocumentLibraryName(node.FullPath, this.tbSPUrl.Text);
                folderTaxonomy = folderTaxonomy.Replace(@"\", "/"); //sharepoint only know "/"

                GSPListItems = WSHelper.GetAllItemsInDocLib(docLib, folderTaxonomy, out GSPFolders);

                if (GSPFolders.Count != 0)
                {
                    foreach (GSPFolder folder in GSPFolders)
                    {
                        #region loading folder

                            GSPTreeNode newFolderNode = new GSPTreeNode(folder.Name);
                            newFolderNode.Tag = folder.SiteSlashParentFolder;
                            newFolderNode.ImageIndex = GSPConstants.ImagePathIndex.FolderIndex;
                            newFolderNode.SelectedImageIndex = GSPConstants.ImagePathIndex.FolderIndex;
                            newFolderNode.siteSlashParentFolder = folder.SiteSlashParentFolder;
                            newFolderNode.Nodes.Add(GSPConstants.TreeView.LazyLoadedChild); //add a dummy child
                            node.Nodes.Add(newFolderNode);

                            #endregion
                    }
                }
                if (GSPListItems.Count != 0)
                {
                    foreach (GSPListItem item in GSPListItems)
                    {
                        #region loading files

                        GSPTreeNode newFileNode = new GSPTreeNode(item.FileName);
                        newFileNode.Tag = item.WebFullUrl;
                        newFileNode.ImageIndex = GSPConstants.ImagePathIndex.GenericFileIndex;
                        newFileNode.SelectedImageIndex = GSPConstants.ImagePathIndex.GenericFileIndex;
                        node.Nodes.Add(newFileNode);

                        #endregion
                    }
                }
            }
        }

        #endregion      
    }
}